gsk: Rework how GLSL shaders are built
authorEmmanuele Bassi <ebassi@gnome.org>
Sun, 3 Jul 2016 20:12:22 +0000 (21:12 +0100)
committerEmmanuele Bassi <ebassi@gnome.org>
Tue, 18 Oct 2016 10:49:07 +0000 (11:49 +0100)
The GL renderer should build the GLSL shaders using GskShaderBuilder.
This allows us to separate the common parts into separate files, and
assemble them as necessary, instead of shipping one big shader per type
of GL API (GL3, GL legacy, and GLES).

13 files changed:
gsk/gskglrenderer.c
gsk/resources/glsl/blend.fs.glsl [new file with mode: 0644]
gsk/resources/glsl/blend.vs.glsl [new file with mode: 0644]
gsk/resources/glsl/es2_common.fs.glsl [new file with mode: 0644]
gsk/resources/glsl/es2_common.vs.glsl [new file with mode: 0644]
gsk/resources/glsl/gl3-base.fs.glsl [deleted file]
gsk/resources/glsl/gl3-base.vs.glsl [deleted file]
gsk/resources/glsl/gl3_common.fs.glsl [new file with mode: 0644]
gsk/resources/glsl/gl3_common.vs.glsl [new file with mode: 0644]
gsk/resources/glsl/gl_common.fs.glsl [new file with mode: 0644]
gsk/resources/glsl/gl_common.vs.glsl [new file with mode: 0644]
gsk/resources/glsl/gles-base.fs.glsl [deleted file]
gsk/resources/glsl/gles-base.vs.glsl [deleted file]

index d9d6540eb850adbb83d9b9b83167415088e33cf2..fd8ee8db9e3be57f484e553e8a46b1a548f35197 100644 (file)
@@ -7,6 +7,7 @@
 #include "gskrendererprivate.h"
 #include "gskrendernodeprivate.h"
 #include "gskrendernodeiter.h"
+#include "gskshaderbuilderprivate.h"
 
 #include "gskprivate.h"
 
@@ -275,131 +276,135 @@ gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
   self->has_buffers = FALSE;
 }
 
-static guint
-create_shader (int         type,
-               const char *code)
+static gboolean
+gsk_gl_renderer_create_programs (GskGLRenderer *self)
 {
-  guint shader;
-  int status;
-
-  shader = glCreateShader (type);
-  glShaderSource (shader, 1, &code, NULL);
-  glCompileShader (shader);
-
-  glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
-  if (status == GL_FALSE)
-    {
-      int log_len;
-      char *buffer;
+  GskShaderBuilder *builder;
+  const char *vertex_preamble;
+  const char *fragment_preamble;
+  int vertex_id = -1, fragment_id = -1;
+  int program_id = -1;
+  GError *error = NULL;
+  gboolean res = FALSE;
+  enum {
+    MVP,
+    MAP,
+    PARENT_MAP,
+    ALPHA,
+    BLEND_MODE,
+    N_UNIFORMS
+  };
+  enum {
+    POSITION,
+    UV,
+    N_ATTRIBUTES
+  };
+  GQuark uniforms[N_UNIFORMS];
+  GQuark attributes[N_ATTRIBUTES];
+  
+  builder = gsk_shader_builder_new ();
 
-      glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);
+  gsk_shader_builder_set_resource_base_path (builder, "/org/gtk/libgsk/glsl");
 
-      buffer = g_malloc0 (log_len + 1);
-      glGetShaderInfoLog (shader, log_len, NULL, buffer);
+  uniforms[MVP] = gsk_shader_builder_add_uniform (builder, "mvp");
+  uniforms[MAP] = gsk_shader_builder_add_uniform (builder, "map");
+  uniforms[PARENT_MAP] = gsk_shader_builder_add_uniform (builder, "parentMap");
+  uniforms[ALPHA] = gsk_shader_builder_add_uniform (builder, "alpha");
+  uniforms[BLEND_MODE] = gsk_shader_builder_add_uniform (builder, "blendMode");
+  
+  attributes[POSITION] = gsk_shader_builder_add_attribute (builder, "position");
+  attributes[UV] = gsk_shader_builder_add_attribute (builder, "uv");
 
-      g_critical ("Compile failure in %s shader:\n%s",
-                  type == GL_VERTEX_SHADER ? "vertex" : "fragment",
-                  buffer);
-      g_free (buffer);
+#define SHADER_VERSION_GLES             110
+#define SHADER_VERSION_GL_LEGACY        120
+#define SHADER_VERSION_GL3              150
 
-      glDeleteShader (shader);
+  if (gdk_gl_context_get_use_es (self->context))
+    {
+      gsk_shader_builder_set_version (builder, SHADER_VERSION_GLES);
+      gsk_shader_builder_add_define (builder, "GSK_GLES", "1");
 
-      return 0;
+      vertex_preamble = "gles_common.vs.glsl";
+      fragment_preamble = "gles_common.fs.glsl";
     }
-
-  return shader;
-}
-
-static void
-gsk_gl_renderer_create_program (GskGLRenderer *self)
-{
-  guint vertex_shader = 0, fragment_shader = 0;
-  const char *fs_path, *vs_path;
-  GBytes *source;
-  int status;
-
-  if (gdk_gl_context_get_use_es (self->context))
+  else if (gdk_gl_context_is_legacy (self->context))
     {
-      vs_path = "/org/gtk/libgsk/glsl/gles-base.vs.glsl";
-      fs_path = "/org/gtk/libgsk/glsl/gles-base.fs.glsl";
+      gsk_shader_builder_set_version (builder, SHADER_VERSION_GL_LEGACY);
+      gsk_shader_builder_add_define (builder, "GSK_LEGACY", "1");
+
+      vertex_preamble = "gl_common.vs.glsl";
+      fragment_preamble = "gl_common.fs.glsl";
     }
   else
     {
-      vs_path = "/org/gtk/libgsk/glsl/gl3-base.vs.glsl";
-      fs_path = "/org/gtk/libgsk/glsl/gl3-base.fs.glsl";
-    }
-
-  GSK_NOTE (OPENGL, g_print ("Compiling vertex shader\n"));
-  source = g_resources_lookup_data (vs_path, 0, NULL);
-  vertex_shader = create_shader (GL_VERTEX_SHADER, g_bytes_get_data (source, NULL));
-  g_bytes_unref (source);
-  if (vertex_shader == 0)
-    goto out;
+      gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3);
+      gsk_shader_builder_add_define (builder, "GSK_GL3", "1");
 
-  GSK_NOTE (OPENGL, g_print ("Compiling fragment shader\n"));
-  source = g_resources_lookup_data (fs_path, 0, NULL);
-  fragment_shader = create_shader (GL_FRAGMENT_SHADER, g_bytes_get_data (source, NULL));
-  g_bytes_unref (source);
-  if (fragment_shader == 0)
-    goto out;
+      vertex_preamble = "gl3_common.vs.glsl";
+      fragment_preamble = "gl3_common.fs.glsl";
+    }
 
-  self->program_id = glCreateProgram ();
-  glAttachShader (self->program_id, vertex_shader);
-  glAttachShader (self->program_id, fragment_shader);
-  glLinkProgram (self->program_id);
+#ifdef G_ENABLE_DEBUG
+  if (GSK_RENDER_MODE_CHECK (SHADERS))
+    gsk_shader_builder_add_define (builder, "GSK_DEBUG", "1");
+#endif
 
-  glGetProgramiv (self->program_id, GL_LINK_STATUS, &status);
-  if (status == GL_FALSE)
+  vertex_id = gsk_shader_builder_compile_shader (builder, GL_VERTEX_SHADER,
+                                                 vertex_preamble,
+                                                 "blend.vs.glsl",
+                                                 &error);
+  if (error != NULL)
     {
-      char *buffer = NULL;
-      int log_len = 0;
-
-      glGetProgramiv (self->program_id, GL_INFO_LOG_LENGTH, &log_len);
-
-      buffer = g_malloc0 (log_len + 1);
-      glGetProgramInfoLog (self->program_id, log_len, NULL, buffer);
-
-      g_critical ("Linking failure in shader:\n%s", buffer);
-      g_free (buffer);
+      g_critical ("Unable to compile vertex shader: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
 
-      glDeleteProgram (self->program_id);
-      self->program_id = 0;
+  fragment_id = gsk_shader_builder_compile_shader (builder, GL_FRAGMENT_SHADER,
+                                                   fragment_preamble,
+                                                   "blend.fs.glsl",
+                                                   &error);
+  if (error != NULL)
+    {
+      g_critical ("Unable to compile fragment shader: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
 
+  program_id = gsk_shader_builder_create_program (builder,
+                                                  vertex_id, fragment_id,
+                                                  &error);
+  if (error != NULL)
+    {
+      g_critical ("Unable to create program: %s", error->message);
+      g_error_free (error);
       goto out;
     }
 
-  /* Find the location of each uniform and attribute we use in our
-   * shaders
-   */
-  self->mvp_location = glGetUniformLocation (self->program_id, "mvp");
-  self->map_location = glGetUniformLocation (self->program_id, "map");
-  self->parentMap_location = glGetUniformLocation (self->program_id, "parentMap");
-  self->alpha_location = glGetUniformLocation (self->program_id, "alpha");
-  self->position_location = glGetAttribLocation (self->program_id, "position");
-  self->uv_location = glGetAttribLocation (self->program_id, "uv");
-  self->blendMode_location = glGetAttribLocation (self->program_id, "blendMode");
-
-  GSK_NOTE (OPENGL, g_print ("Program [%d] { mvp:%u, map:%u, alpha:%u, position:%u, uv:%u }\n",
-                             self->program_id,
-                             self->mvp_location,
-                             self->map_location,
-                             self->alpha_location,
-                             self->position_location,
-                             self->uv_location));
-
-  /* We can detach and destroy the shaders from the linked program */
-  glDetachShader (self->program_id, vertex_shader);
-  glDetachShader (self->program_id, fragment_shader);
+  self->program_id = program_id;
+  self->mvp_location = gsk_shader_builder_get_uniform_location (builder, uniforms[MVP]);
+  self->map_location = gsk_shader_builder_get_uniform_location (builder, uniforms[MAP]);
+  self->parentMap_location = gsk_shader_builder_get_uniform_location (builder, uniforms[PARENT_MAP]);
+  self->alpha_location = gsk_shader_builder_get_uniform_location (builder, uniforms[ALPHA]);
+  self->blendMode_location = gsk_shader_builder_get_uniform_location (builder, uniforms[BLEND_MODE]);
+  self->position_location = gsk_shader_builder_get_attribute_location (builder, attributes[POSITION]);
+  self->uv_location = gsk_shader_builder_get_attribute_location (builder, attributes[UV]);
+
+  res = TRUE;
 
 out:
-  if (vertex_shader != 0)
-    glDeleteShader (vertex_shader);
-  if (fragment_shader != 0)
-    glDeleteShader (fragment_shader);
+  if (vertex_id > 0)
+    glDeleteShader (vertex_id);
+  if (fragment_id > 0)
+    glDeleteShader (fragment_id);
+
+  g_object_unref (builder);
+
+  return res;
 }
 
 static void
-gsk_gl_renderer_destroy_program (GskGLRenderer *self)
+gsk_gl_renderer_destroy_programs (GskGLRenderer *self)
 {
   if (self->program_id != 0)
     {
@@ -447,7 +452,8 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
 
   GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
   gsk_gl_renderer_create_buffers (self);
-  gsk_gl_renderer_create_program (self);
+  if (!gsk_gl_renderer_create_programs (self))
+    return FALSE;
 
   return TRUE;
 }
@@ -466,7 +472,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
   g_clear_pointer (&self->transparent_render_items, g_array_unref);
 
   gsk_gl_renderer_destroy_buffers (self);
-  gsk_gl_renderer_destroy_program (self);
+  gsk_gl_renderer_destroy_programs (self);
 
   if (self->context == gdk_gl_context_get_current ())
     gdk_gl_context_clear_current ();
diff --git a/gsk/resources/glsl/blend.fs.glsl b/gsk/resources/glsl/blend.fs.glsl
new file mode 100644 (file)
index 0000000..5d2f873
--- /dev/null
@@ -0,0 +1,22 @@
+vec3 BlendMultiply(vec3 Csrc, vec3 Cdst) {
+  return Csrc * Cdst;
+}
+
+void main() {
+  vec4 src = Texture(map, vUv);
+  vec4 mask = Texture(parentMap, vUv);
+  vec3 res;
+
+  if (blendMode == 0) {
+    res = src.xyz;
+  }
+  else if (blendMode == 1) {
+    res = BlendMultiply(src.xyz, mask.xyz);
+  }
+  else {
+    // Use red for debugging missing blend modes
+    res = vec3(1.0, 0.0, 0.0);
+  }
+
+  setOutputColor(vec4(res, src.a * alpha));
+}
diff --git a/gsk/resources/glsl/blend.vs.glsl b/gsk/resources/glsl/blend.vs.glsl
new file mode 100644 (file)
index 0000000..7ec0594
--- /dev/null
@@ -0,0 +1,6 @@
+void main() {
+  gl_Position = mvp * vec4(position, 0.0, 1.0);
+
+  // Flip the sampling
+  vUv = vec2(uv.x, 1 - uv.y);
+}
diff --git a/gsk/resources/glsl/es2_common.fs.glsl b/gsk/resources/glsl/es2_common.fs.glsl
new file mode 100644 (file)
index 0000000..c09f75e
--- /dev/null
@@ -0,0 +1,16 @@
+precision mediump float;
+
+uniform mat4 mvp;
+uniform sampler2D map;
+uniform sampler2D parentMap;
+uniform float alpha;
+
+varying vec2 vUv;
+
+vec4 Texture(sampler2D sampler, vec2 texCoords) {
+  return texture2D(sampler, texCoords);
+}
+
+void setOutputColor(vec4 color) {
+  gl_FragColor = color;
+}
diff --git a/gsk/resources/glsl/es2_common.vs.glsl b/gsk/resources/glsl/es2_common.vs.glsl
new file mode 100644 (file)
index 0000000..e52c153
--- /dev/null
@@ -0,0 +1,7 @@
+uniform mat4 mvp;
+uniform float alpha;
+
+attribute vec2 position;
+attribute vec2 uv;
+
+varying vec2 vUv;
diff --git a/gsk/resources/glsl/gl3-base.fs.glsl b/gsk/resources/glsl/gl3-base.fs.glsl
deleted file mode 100644 (file)
index 0127556..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#version 150
-
-smooth in vec2 vUv;
-
-out vec4 outputColor;
-
-uniform mat4 mvp;
-uniform sampler2D map;
-uniform sampler2D parentMap;
-uniform float alpha;
-uniform int blendMode;
-
-vec3 BlendMultiply(vec3 Csrc, vec3 Cdst) {
-  return Csrc * Cdst;
-}
-
-
-void main() {
-  vec4 src = texture2D(map, vUv);
-  vec4 dst = texture2D(parentMap, vUv);
-  vec3 res;
-
-  if (blendMode == 0) {
-    res = src.xyz;
-  }
-  else if (blendMode == 1) {
-    res = BlendMultiply(src.xyz, dst.xyz);
-  }
-  else {
-    res = vec3(1.0, 1.0, 0.0);
-  }
-
-  outputColor = vec4(res, src.a * alpha);
-}
diff --git a/gsk/resources/glsl/gl3-base.vs.glsl b/gsk/resources/glsl/gl3-base.vs.glsl
deleted file mode 100644 (file)
index 534f201..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#version 150
-
-uniform mat4 mvp;
-uniform sampler2D map;
-uniform float alpha;
-
-in vec2 position;
-in vec2 uv;
-
-smooth out vec2 vUv;
-
-void main() {
-  gl_Position = mvp * vec4(position, 0.0, 1.0);
-
-  vUv = vec2(uv.x, 1 - uv.y);
-}
diff --git a/gsk/resources/glsl/gl3_common.fs.glsl b/gsk/resources/glsl/gl3_common.fs.glsl
new file mode 100644 (file)
index 0000000..48134a3
--- /dev/null
@@ -0,0 +1,19 @@
+precision highp float;
+
+uniform sampler2D map;
+uniform sampler2D parentMap;
+uniform mat4 mvp;
+uniform float alpha;
+uniform int blendMode;
+
+in vec2 vUv;
+
+out vec4 outputColor;
+
+vec4 Texture(sampler2D sampler, vec2 texCoords) {
+  return texture(sampler, texCoords);
+}
+
+void setOutputColor(vec4 color) {
+  outputColor = color;
+}
diff --git a/gsk/resources/glsl/gl3_common.vs.glsl b/gsk/resources/glsl/gl3_common.vs.glsl
new file mode 100644 (file)
index 0000000..96289e6
--- /dev/null
@@ -0,0 +1,7 @@
+uniform mat4 mvp;
+uniform float alpha;
+
+in vec2 position;
+in vec2 uv;
+
+out vec2 vUv;
diff --git a/gsk/resources/glsl/gl_common.fs.glsl b/gsk/resources/glsl/gl_common.fs.glsl
new file mode 100644 (file)
index 0000000..e9cfa44
--- /dev/null
@@ -0,0 +1,14 @@
+uniform mat4 mvp;
+uniform sampler2D map;
+uniform sampler2D parentMap;
+uniform float alpha;
+
+varying vec2 vUv;
+
+vec4 Texture(sampler2D sampler, vec2 texCoords) {
+  return texture2D(sampler, texCoords);
+}
+
+void setOutputColor(vec4 color) {
+  gl_FragColor = color;
+}
diff --git a/gsk/resources/glsl/gl_common.vs.glsl b/gsk/resources/glsl/gl_common.vs.glsl
new file mode 100644 (file)
index 0000000..e52c153
--- /dev/null
@@ -0,0 +1,7 @@
+uniform mat4 mvp;
+uniform float alpha;
+
+attribute vec2 position;
+attribute vec2 uv;
+
+varying vec2 vUv;
diff --git a/gsk/resources/glsl/gles-base.fs.glsl b/gsk/resources/glsl/gles-base.fs.glsl
deleted file mode 100644 (file)
index ff43db9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-precision mediump float;
-
-uniform mat4 mvp;
-uniform sampler2D map;
-uniform float alpha;
-
-varying vec2 vUv;
-
-void main() {
-  gl_FragColor = texture2D(map, vUv) * vec4(alpha);
-}
diff --git a/gsk/resources/glsl/gles-base.vs.glsl b/gsk/resources/glsl/gles-base.vs.glsl
deleted file mode 100644 (file)
index 747e05e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-uniform mat4 mvp;
-
-attribute vec2 position;
-attribute vec2 uv;
-
-varying vec2 vUv;
-
-void main() {
-  gl_Position = mvp * vec4(position, 0.0, 1.0);
-
-  vUv = vec2(uv.x, 1.0 - uv.y);
-}